#include <iostream>
#include <string>
#include <typeinfo>

// C语言风格的格式转换
int main1()
{
	int i = 1;
	// 隐式类型转换
	// 隐式类型转换主要发生在整形和整形之间，整形和浮点数之间，浮点数和浮点数之间
	double d = i;
	printf("%d, %.2f\n", i, d);

	int* p = &i;
	// 显示的强制类型转换
	// 强制类型转换主要发生在指针和整形之间，指针和指针之间
    // g++编译器下也不允许
	//int address = (int)p;
	//printf("%p, %d\n", p, address);

	// 编译报错：类型强制转换: 无法从“int *”转换为“double”
	// 指针是地址的编号，也是一种整数，所以可以和整形互相转换
	// 但是指针和浮点数毫无关联，强转也是不支持的
	//d = (double)p;

	return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

// C++风格的类型转换
class A
{
public:
    A(int x) :_a(x) {}
    A(int x, int y) :_a(x), _b(y) {}
    operator int() { return _a + _b; }
private:
    int _a;
    int _b;
};

class B
{
public:
    B(const A) {}
private:
    std::string s;
};

// 1.内置类型转换为自定义类型
int main2()
{
    // C++中，内置类型转换为自定义类型，需要有构造函数支持
    // 隐式类型转换，本质上是由1构造了一个临时对象，然后进行拷贝构造，编译器优化为一个构造
    A aa = 1; 

    // 多参数进行隐式类型转换要使用花括号
    A aa1 = {1, 2};
    return 0;
}

// 2.自定义类型转换为内置类型，需要自定义类型重载operator()
// 但是这里重载operator()时需要进行特殊处理，格式为operator type(){}
int main3()
{
    A aa = 1;
    int b = aa; // 没有operator int() 成员函数，就会报错
    return 0;
}

// 3.自定义类型之间也可以进行转化，只需要有特定参数的构造函数即可
// 如果想让A转化为B，则需要在B中实现以A为参数的构造函数
int main4()
{
    A aa = {1, 2};
    B b = aa;
    return 0;
}

class people
{
    virtual void get() {}
private:
    std::string _name;
};

class student : public people
{};

// 4.C/C++不是类型安全的语言，因为其支持类型转换
// C++规范了显示类型转换的方式
int main()
{
    // static_cast<>() 用于两个含义相近的类型进行转换，要求转换是有意义的
    // 基本类型之间的转换， 或者有继承关系的类之间进行转换
    // 比如：int->double, 两者都是表示数值大小，可以进行转换
    int a = 10;
    double b = static_cast<double>(a);
    std::cout << "a->" << typeid(a).name() << std::endl;
    std::cout << "b->" << typeid(b).name() << std::endl;

    // reinterpret_cast 用于进行底层的类型转换，将一种类型的指针或引用转换为另一种类型，不进行类型检查，风险较高。
    int *ptr = &a;
    double *d_ptr = reinterpret_cast<double*>(ptr); // 将int* 转换为 double*
    std::cout << "ptr->" << typeid(ptr).name() << std::endl;
    std::cout << "d_ptr->" << typeid(d_ptr).name() << std::endl;

    // const_cast 用于取出指针/引用的const属性，或者添加const属性
    volatile const int c = 100; // 如果不加volatile关键字，编译器识别到const，就会将其直接放在寄存器中，而不从内存中读取
    int * c_ptr = const_cast<int*>(&c);
    *c_ptr = 1;
    std::cout << "c->" << c << std::endl;
    std::cout << "c_ptr->" << *c_ptr << std::endl;

    // dynamic_cast 用于基类和派生类之间指针/引用的转换，要求基类必须是多态类型——有虚函数
    // 如果是派生类转换为基类，则转化成功
    // 如果是基类转化为派生类，转化失败，指针转化返回nullptr， 引用转化抛出异常bad_cast
    student *s = new student();
    people *p = dynamic_cast<people*>(s);
    
    // 因为多态类型，有虚函数表，在底层可以用虚表中的type_info来判断指向的是基类的指针还是派生类的指针

    return 0;
}